#coding:utf-8
import numpy as np
import serial

import socket
import time
import sys
import threading
import re
import re, errno

#   !!!  部署之前需要修改一下193行的当前运行环境上的串口设备名 

def query_local_ip():
    st = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:       
        st.connect(('8.8.8.8', 80))
        IP = st.getsockname()[0]
    except Exception:
        IP = '0.0.0.0'
    finally:
        st.close()
    return IP

class UartRx2UpsoftThread(threading.Thread):
    ''' 读取串口数据并转发给上位机线程
    '''
    def __init__(self,sock=None,decoder=None,daemon=None):
        super(UartRx2UpsoftThread, self).__init__(daemon=daemon)
        self.__is_running = True
        self.daemon = daemon
        self.sock = sock
        self.decoder = decoder
        
    def terminate(self):
        self.__is_running = False
        
    def run(self):
        if self.decoder.ser.isOpen() == False:
            self.decoder.ser.open()
        while( self.__is_running ):
            try:
                msg = self.decoder.getdata()
                #[170 255   1  13  10   0  11   0 ]
                print("serial raw data : %s" % msg)
                if len(msg) > 0:
                    msg2 = str(msg)
                    self.sock.send(msg2.encode('utf-8'))
            except Exception as err:
                print('Uart Rx thread exception ~~~ ')
                print(err)
        if(self.decoder.ser.isOpen()):
            self.decoder.ser.close()




class SerialDecoder(object):
    """
    初始化参数：
        bufsize：缓存区长度，可根据需要解析的包大小调节
        com:串口号，是字符串，例如'COM9'
        band：波特率，
        debug：是否输出调试信息
    功能：
        实现对串口协议的解析，协议内容：
            帧头：
                0xAA 0xAA, 两个字节
            功能字：
                0x02, 一个字节
            数据长度：
                0x0A, 一个字节，代表数据内容长度，必定为偶数
            数据内容：
                字节个数不确定，每两个字节组成一个16位无符号整形数
                高位在前，地位在后
            校验和：
                一个字节，为前面所以字节的和取低八位
    """

    def __init__(self, bufsize=200, com='/dev/ttyAMA0', band=9600, debug=False):
        """
        输入参数：
            bufsize：缓存区长度，可根据需要解析的包大小调节
            com:串口号，是字符串，例如'COM9'
            band：波特率，
            debug：是否输出调试信息
        """
        self.current = 0  # 当前buf中数据长度
        self.bufsize = bufsize
        self.buf = [0 for i in range(bufsize)]  # 保存数据buf,必须按照长度初始化
        self.data_signal = []  # 解析到的有效数据
        self.debug = debug  # 是否输出调试信息
        self.newdata = False  # 自上次取获取数据后是否解析出新的的数据
        self.ser = serial.Serial(com, band)

    def getdata(self):  # 获取解析到的数据
        xlen = 0
        while(xlen == 0):
            serialData = self.ser.read_all()
            xlen = len(serialData)
        self.append(serialData)
        self.newdata = False     # 作用何在？？
        return self.data_signal

    def append(self, serialbuf):  # 把串口数据加入到buf中，加入完成后尝试进行解析
        filebuf = np.frombuffer(serialbuf, dtype=np.uint8)
        '''
        >>> s = b'\xaa\xff'
        >>> a = np.frombuffer(s,dtype=np.uint8)
        array([170, 255], dtype=uint8)
        '''
        if self.current + filebuf.size > self.bufsize:    # 溢出
            self.buf[self.current:self.bufsize] =   \
                filebuf[0:self.bufsize - self.current - filebuf.size]
            self.current = self.bufsize
        else:
            self.buf[self.current:self.current + filebuf.size] = filebuf
            self.current += filebuf.size
        self.process()

    def process(self):  # 进行数据解析
        # self.ID_data = [0x01, 0x02, 0x03, 0x05, 0x07, 0x08, 0x20]
        if self.current <= 35:  # 如果队列中数据多于2包数据
            return 1
        #if self.buf[0] == 170 and self.buf[1] == 255 and self.buf[2] in self.ID_data:
        if self.buf[0] == 170 and self.buf[1] == 170:
            if self.debug:
                print("检测到帧头，功能字是" + str(self.buf[2]))
            datalength = self.buf[3]  # 有效数据长度
            framelength = datalength + 5  # 帧长度    //  6 =  两个头  +  功能帧  数据长度  1个校验位
            #cal from buf[0]
            datasum = np.sum(self.buf[0:framelength - 1]) % 256   #datasum1 is the sum of the 1th
        
            if datasum == self.buf[framelength - 1]:  # two sum byte check 校验通过
                self.data_signal = self.buf[0:4 + datalength]
                self.data_signal = np.array(
                    self.data_signal, dtype='uint8')
                print("current1:%s" % self.current)
                self.newdata = True
                if self.debug:
                    print(self.data_signal)
                self.buf = np.roll(self.buf, -framelength) #将buf，沿着axis的方向，滚动framlength长度） put the data from head to the tail 
                self.current -= framelength
                if self.debug:
                    print("解析到一帧数据")
                print("current2:%s" % self.current)
            else:  # 校验失败
                if self.debug:
                    print("校验和错误")
                if 170 in self.buf[0:self.current]:  # 帧头对，但是校验和错误  170   255 choose 255 is enough maybe
                    temparray = self.buf[2:self.current]
                    if not isinstance(temparray, list):
                        temparray = temparray.tolist()
                    offset = temparray.index(170)
                    self.buf = np.roll(self.buf, -offset)
                    self.current -= offset
                    print("current3:%s" % self.current)
        # 如果解析不到，舍弃前面的数据，直到data[0] == 170
        elif 170 in self.buf[0:self.current]:
            #print('1')
            if self.debug:
                print("接收到无效数据")
            temparray = self.buf[0:self.current]
            if not isinstance(temparray, list):
                temparray = temparray.tolist()
            #else:
                #pass
            
            offset = temparray.index(170)
            self.buf = np.roll(self.buf, -offset)
            self.current -= offset



# #####################################################################
#             下面开始才是正式执行语句
# #####################################################################
host_ip = '0.0.0.0'   #query_local_ip()
port = 55548

host_addr = (host_ip,port)

socket_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket_tcp.bind(host_addr)
socket_tcp.listen(1)

def main_func():
    while(True):
        print("监听端口 55548 等待连接...")
        socket_con,(client_ip, client_port) = socket_tcp.accept()
        print("Connection accepted from %s." %client_ip)
        
        sd1 = SerialDecoder(500, 'COM6', 256000, True)
        # sd2 = SerialDecoder(500, '/dev/ttyAMA0', 115200, True)
        
        rx_thread = UartRx2UpsoftThread(socket_con,sd1,True)
        rx_thread.start()
        
        #sr_read_thead = threading.Thread(target=ser_read_and_uptrans,daemon=True)
        #sr_read_thead.start()
        
        print("Receiving package...")
        while True:
            try:
                recv_data_1 = socket_con.recv(500).decode()
                # "170" "255"   "1"  "13"  10   0  11   0 ]"
                recv_data_1 = [int(s) for s in re.findall( r'\d+' , str(recv_data_1))]
                # [170 255   1  13  10   0  11   0 ]
                recv_data_2 = [hex(item) for item in recv_data_1]
                # [0xaa 0xaa   0x1  13  10   0  11   0 ]
                send_data_rc = ''  #放循环切片字符串
                send_data_rc_2 = '' #放最终的发送字符串
                #print(2)
                for i in range(len(recv_data_1)):
                    send_data_rc = recv_data_2[i].split('x')[-1]
                    if len(send_data_rc) == 1:
                        send_data_rc = '0' + send_data_rc
                    elif len(send_data_rc) > 2:
                        send_data_rc = send_data_rc[-2:]
                        
                    send_data_rc_2 += send_data_rc
                #print(33)
                send_data_rc_2 += '\0'
                
                data = send_data_rc_2.encode('utf-8')
                if len(data) > 0:
                    #print("Received tcp data:%s" % data)
                    sd1.ser.write(data)

            except Exception as err:
                print(err)
                print('上位机连接断开。。。')
                rx_thread.terminate()
                sd1.ser.close()
                time.sleep(0.2)
                print('重新等待上位机连接~~~')
                break 


main_func()
